#include <cstdlib>
#include <string>
#include <pthread.h>
#include <string>
#include <cstring>

#include "../include/contest_interface.h"
#include "../include/Iterator.h"
#include "../include/Mutex.h"
#include "../include/IndexSchema.h"
#include "../include/Index.h"
#include "../include/tx.h"
#include "../include/util.h"
#include "../include/IteratorLink.h"
#include "../include/AutoCommitTx.h"

/**
 * Starts a new transaction and sets the corresponding handle (tx).
 * 
 * This function needs to allocate all memory that is needed to
 * create the new transaction instance.
 * 
 *  @return ErrorCode
 *- \ref kOk
 *        if the transaction was successfully started
 * - \ref kErrorOutOfMemory
 *        if the maximum number of concurrent transactions has been reached
 * - \ref kErrorGenericFailure
 *        if the transaction could not be started for some other reason
 */
ErrorCode BeginTransaction(Transaction **tx) {
    if (tx == NULL)
        return kErrorGenericFailure;

    try {
        // Begin new transaction with isolation level read committed
        *tx = new Transaction();
        (*tx)->Tx_set_status(2);
        (*tx)->Tx_opt_init();
    } catch (bad_alloc&) {
        return kErrorOutOfMemory;
    } catch (exception &e) {
        return kErrorGenericFailure;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Aborts the given transaction and rolls back all changes
 * made during the course of this transaction.
 * 
 * After calling this, the given transaction pointer will be invalidated and therefore
 * cannot be used by any thread.
 * 
 * Iterators that have been opened in this transaction will be closed
 * (if this has not already happened).
 * 
 * @return ErrorCode
 *  - \ref kOk
 *         if the transaction was successfully aborted
 *  - \ref kErrorTransactionClosed
 *         if there was no transaction to abort
 *  - \ref kErrorGenericFailure
 *         if the transaction could not be aborted for some other reason
 */
ErrorCode AbortTransaction(Transaction **tx) {
    // Check that the given transaction is valid
    if ((tx == NULL) || (*tx == NULL))
        return kErrorTransactionClosed;
    try {
        if ((*tx)->Tx_index() == NULL) {
            delete (*tx);
            (*tx) = NULL;
            return kErrorTransactionClosed;
        }
        // Abort the transaction and reset the handle
        if (((*tx)->Tx_index()->EndTransaction(tx)) != kOk)
            return kErrorGenericFailure;

        delete (*tx);
        (*tx) = NULL;
    } catch (exception &e) {
        return kErrorGenericFailure;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
} //roll back

/**
 * Ends the given transaction and persists all changes
 * made during the course of this transaction. Abort if
 * this is not possible.
 * 
 * After calling this, the given transaction pointer will be invalidated and therefore
 * cannot be used by any thread.
 * 
 * Iterators that have been opened in this transaction will be closed
 * (if this has not already happened).
 * 
 * @return ErrorCode
 * - \ref kOk
 *        if the transaction was successfully committed
 * - \ref kTransactionAborted
 *        if the transaction could not be committed and had to be aborted
 * - \ref kErrorTransactionClosed
 *        if there was no transaction to commit
 * - \ref kErrorGenericFailure
 *        if the transaction could not be committed for some other reason
 */
ErrorCode CommitTransaction(Transaction **tx) {
    // Check that the given transaction is valid
    if ((tx == NULL) || (*tx == NULL))
        return kErrorTransactionClosed;

    try {
        if ((*tx)->Tx_index() == NULL) {
            delete (*tx);
            (*tx) = NULL;
            return kErrorTransactionClosed;
        }
        // Commit the transaction and reset the handle
        ErrorCode ret = (*tx)->Tx_index()->TransactionCommitted(tx);

        delete (*tx);
        (*tx) = NULL;
        return ret;
    } catch (exception &e) {
        return kErrorGenericFailure; //kTransactionAborted;;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Creates an empty index with <attribute_count> columns of the types defined
 * by the given key type.
 *
 * The key type needs to be an ordered array of attribute types, as attributes
 * are not named and therefore are only identified by there position inside the key.
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the index was successfully created
 * - \ref kErrorIndexExists
 *     -->   if an index with the given name already exits
 * - \ref kErrorOutOfMemory
 *     -->   if the operation was not completed because of a lack of free memory
 * - \ref kErrorGenericFailure
 *     -->   if the index could not be created for some other reason
 */
ErrorCode CreateIndex(const char* name, uint8_t attribute_count, KeyType type) {
    // TODO lock map
    if ((!name) || (strlen(name) < 1) || (attribute_count < 1) || (!type))
        return kErrorGenericFailure;

    //  map_indexes singelton = map_indexes::getInstance();
    if (map_indexes::getInstance().test_existance(name)) {
        return kErrorIndexExists;
    }

    try {
        IndexSchema *idx = new IndexSchema(name, attribute_count, type);
        map_indexes::getInstance().insert(name, idx);
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Opens an index specified by its name to be used by the current thread.
 *
 * An index may be used by multiple transactions at a time, therefore concurrency control
 * is necessary.
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the index was successfully opened
 * - \ref kErrorUnknownIndex
 *     -->   if an index with the given name has not been created
 * - \ref kErrorGenericFailure
 *     -->   if the index could not be created for some other reason
 */
ErrorCode OpenIndex(const char* name, Index **idx) {
    if ((name == NULL) || (strlen(name) == 0))
        return kErrorGenericFailure;

    //  map_indexes singelton = map_indexes::getInstance();


    IndexSchema *scheme; // = new IndexSchema(); // Modified @ March 10; 10:27 PM

    try {
        scheme = map_indexes::getInstance().Find(name);
        if (scheme == NULL) {
            delete scheme;
            return kErrorUnknownIndex;
        }
        *idx = new Index(scheme);
        (*idx)->getSchema()->RegisterHandle(*idx);
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Closes a specified index handle.
 *
 * After calling this, the given index pointer will be invalidated and therefore
 * cannot be used by any thread.
 *
 * All open iterators that use the specified index handle will be closed.
 *
 * If the given index has already been closed, the function will return \ref kErrorUnknownIndex.
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the index was successfully closed
 * - \ref kErrorUnknownIndex
 *     -->   if the index has already been closed or never existed
 *  - \ref kErrorGenericFailure
 *     -->   if the index could not be closed for some other reason
 */
ErrorCode CloseIndex(Index **idx) {
    // Check that the given index handle is valid
    if ((idx == NULL) || (*idx == NULL)) {
        return kErrorUnknownIndex;
    }

    if ((*idx)->isClosed()) {
        return kErrorUnknownIndex;
    }

    try {
        (*idx)->Close();
        *idx = NULL;
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Deletes an index given by its name. This should free all resources associated
 * with this index.
 *
 * If the index is currently opened, it needs to be closed before deletion.
 *
 * Attempts to delete indices that have been modified by unresolved (neither
 * committed nor aborted) transactions should return kErrorOpenTransactions.
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the index was successfully deleted
 * - \ref kErrorUnknownIndex
 *     -->   if an index with the given name does not exist
 * - \ref kErrorOpenTransactions
 *     -->   if the index could not be deleted because it has been modified by transactions
 *        that are still open
 * - \ref kErrorGenericFailure
 *     -->   if the index could not be deleted for some other reason
 */
ErrorCode DeleteIndex(const char* name) {
    // Check that the given name is valid
    if ((name == NULL) || (strlen(name) < 1))
        return kErrorGenericFailure;

    try {
        //    map_indexes singelton = map_indexes::getInstance();
        return map_indexes::getInstance().deleteIndex(name);
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

ErrorCode tryInsertRecord(Transaction *tx, Index *idx, Record *record) {
    if (tx->isReject())
        return kErrorGenericFailure;

    if (tx->Tx_index() == NULL)
        tx->Tx_set_index(idx);
    if (!(tx->Tx_isHandled()))
        idx->HandleTransaction(&tx);
    Record myrecord;
    CopyRecord(&myrecord, record);
    Iterator *it = NULL;
    operation *op = new operation(2, it, myrecord, false);

    ErrorCode ret = idx->Insert(tx, &myrecord);
    if (ret == kOk) {
        /*Now the op->ptr has current the previous record before the new one and next the record after the next one*/
        op->done_flag = true;
        tx->insertOpt(op);

        tx->Tx_get_ShadowSpace()->InsertNewRecordList(&myrecord); // Insert this record in shadowSpace
    } else {
        //No need to check the shadow space

        op->done_flag = false;
        tx->insertOpt(op);
        return ret;
    }
    return ret;
}

/**
 * Inserts a record (representing a multidimensional key and an associated payload)
 * into the index.
 *
 * This function might be called without passing a valid transaction handle. In
 * this case all changes are committed immediately (autocommit mode).
 *
 * As changes to the source record should not affect the saved data, your
 * implementation is responsible for copying all necessary data.
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the record was successfully inserted
 * - \ref kErrorUnknownIndex
 *     -->   if the index has been closed already or never existed
 * - \ref kErrorIncompatibleKey
 *     -->   if the record that should be inserted is not compatible with the given index
 * - \ref kErrorDeadlock
 *     -->   if the call could not be completed because of a deadlock
 * - \ref kErrorOutOfMemory
 *     -->   if the operation was not completed because of a lack of free memory
 * - \ref kErrorGenericFailure
 *     -->   if the record could not be inserted for some other reason
 */
ErrorCode InsertRecord(Transaction *tx, Index *idx, Record *record) {
    // Check that all input values are valid
    if ((idx == NULL) || (idx->isClosed()))
        return kErrorUnknownIndex;

    if (!idx->getSchema()->Compatible(record))
        return kErrorIncompatibleKey;

    try {
        if (tx == NULL) {
            //Auto Commit operation
            ErrorCode errRet = AutoCommitInsertRecord(idx, record);
            return errRet;

        } else {
            return tryInsertRecord(tx, idx, record);
        }
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }

}

ErrorCode tryUpdateRecord(Transaction *tx, Index *idx, Record *record, Block *new_payload,
        uint8_t flags) {
    //    if (tx->isReject())
    //        return kErrorGenericFailure;

    if (tx->Tx_index() == NULL)
        tx->Tx_set_index(idx);
    if (!(tx->Tx_isHandled()))
        idx->HandleTransaction(&tx);
    Record myrecord;
    CopyRecord(&myrecord, record);

    Iterator *it = new Iterator(idx, record->key, record->key);
    operation *op = new operation(3, it, myrecord, false, flags);

    CopyPayload(op->r.payload, *new_payload);

    ErrorCode ret = kOk;
    // I should look for the record in the index 
    // if I can not find it on the ShadowSpace
    //    if (((!tx->Tx_get_ShadowSpace()->checkExistance(&myrecord, flags)))
    ret = idx->UpdateRecord(tx, &myrecord, flags, op->ptr);

    if (ret == kOk) {
        /*Now the op->ptr has current the previous record before the new one and next the record after the next one*/
        op->done_flag = true;
        tx->insertOpt(op);
        //        tx->Tx_get_ShadowSpace()->Update(&myrecord, new_payload, flags); // Insert this record in shadowSpace UPdate LIst
    } else {
        //Need to check the shadow space
        //        operation* op = tx->Tx_find_Record(record);
        //        if(op == NULL){
        op->done_flag = false;
        tx->insertOpt(op);
        return ret;
        //        }      
    }
    return ret;
}

/**
 * Searches for a key/value combination given as a record and updates its value.
 *
 * If the record was not found, an appropriate ErrorCode (\ref kErrorNotFound)
 * will be returned.
 *
 * This function might be called without passing a valid transaction handle. In this case all
 * changes are committed immediately (autocommit mode).
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the record was successfully updated
 * - \ref kErrorUnknownIndex
 *     -->   if the given index has been closed already or never existed
 * - \ref kErrorIncompatibleKey
 *     -->   if the record that should be updated is not compatible with the given index
 * - \ref kErrorNotFound
 *     -->   if the given record could not be found
 * - \ref kErrorDeadlock
 *     -->   if the call could not be completed because of a deadlock
 * - \ref kErrorOutOfMemory
 *     -->   if the operation was not completed because of a lack of free memory
 * - \ref kErrorGenericFailure
 *     -->   if the record could not be updated for some other reason
 */
ErrorCode UpdateRecord(Transaction *tx, Index *idx, Record *record, Block *new_payload,
        uint8_t flags) {
    // Check that all input values are valid
    if ((idx == NULL) || idx->isClosed())
        return kErrorUnknownIndex;

    if (!idx->getSchema()->Compatible(record))
        return kErrorIncompatibleKey;

    if (new_payload == NULL)
        return kErrorGenericFailure;

    try {
        if (tx == NULL) {
            ErrorCode ret;
            Transaction *tx_a;
            ret = BeginTransaction(&tx_a);

            if (ret == kOk) {
                if (tx_a->Tx_index() == NULL)
                    tx_a->Tx_set_index(idx);
                if (!(tx_a->Tx_isHandled()))
                    idx->HandleTransaction(&tx_a);
                tx_a->Tx_set_autoCommit();
                ret = tryUpdateRecord(tx_a, idx, record, new_payload, flags);
            }

            ret = CommitTransaction(&tx_a);
            if (ret != kOk)
                return kErrorGenericFailure;
            return ret;
        } else {
            return tryUpdateRecord(tx, idx, record, new_payload, flags);
        }
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

ErrorCode tryDeleteRecord(Transaction *tx, Index *idx, Record *record, uint8_t flags) { // Check that all input values are valid
    // Check that all input values are valid
    if (tx->isReject())
        return kErrorGenericFailure;

    if (tx->Tx_index() == NULL)
        tx->Tx_set_index(idx);

    if (!(tx->Tx_isHandled()))
        idx->HandleTransaction(&tx);

    Record myrecord;
    CopyRecord(&myrecord, record);
    Iterator *it = new Iterator(idx, record->key, record->key);
    operation *op = new operation(1, it, myrecord, false, flags);

    ErrorCode ret = kOk;
    // I should look for the record in the index if the transaction is auto commited
    // or if I can not find it on the ShadowSpace
    //    if ((tx->Tx_is_autoCommit())||(!tx->Tx_get_ShadowSpace()->checkExistance(&myrecord, flags)))

    ret = idx->DeleteRecord(tx, &myrecord, flags, op->ptr);

    if (ret == kOk) {
        /*Now the op->ptr has current the previous record before the new one and next the record after the next one*/
        op->done_flag = true;
        tx->insertOpt(op);

        // Insert this record in shadowSpace Delete LIst
        //        tx->Tx_get_ShadowSpace()->Delete(&myrecord, flags);
    } else {
        //need to check the shadow space
        op->done_flag = false;
        // tx->insertOpt(op);            //COmmented by Basmah, in case of failure of delete function, i dont need to add it in OP list @ 1:01 am 11 Mar
        return ret;
    }
    return ret;
}

/**
 * Searches for a record and removes it from the index.
 *
 * If the record was not found, an appropriate ErrorCode (\ref kErrorNotFound)
 * will be returned.
 *
 * This function might be called without passing a valid transaction handle. In this case all
 * changes are committed immediately (autocommit mode).
 *
 * @return ErrorCode
 * - \ref kOK
 *     -->   if the record was successfully deleted
 * - \ref kErrorUnknownIndex
 *     -->   if the given index has been closed already or never existed
 * - \ref kErrorIncompatibleKey
 *     -->   if the record that should be deleted is not compatible with the given index
 * - \ref kErrorNotFound
 *     -->   if the given record could not be found
 * - \ref kErrorDeadlock
 *     -->   if the call could not be completed because of a deadlock
 * - \ref kErrorGenericFailure
 *     -->   if the record could not be deleted for some other reason
 */
ErrorCode DeleteRecord(Transaction *tx, Index *idx, Record *record, uint8_t flags) { // Check that all input values are valid
    // Check that all input values are valid
    if ((idx == NULL) || idx->isClosed())
        return kErrorUnknownIndex;

    if (!idx->getSchema()->Compatible(record))
        return kErrorIncompatibleKey;

    try {
        if (tx == NULL) {
            ErrorCode ret;
            Transaction *tx_a;
            ret = BeginTransaction(&tx_a);

            if (ret == kOk) {
                if (tx_a->Tx_index() == NULL)
                    tx_a->Tx_set_index(idx);
                if (!(tx_a->Tx_isHandled()))
                    idx->HandleTransaction(&tx_a);
                tx_a->Tx_set_autoCommit();
                ret = tryDeleteRecord(tx_a, idx, record, flags);
            }

            ret = CommitTransaction(&tx_a);
            if (ret != kOk)
                return kErrorGenericFailure;
            return ret;
        } else {
            return tryDeleteRecord(tx, idx, record, flags);
        }
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

/**
 * Returns an \ref Iterator that starts at the first value of the given minimum
 * multidimensional key.
 *
 * One or more parts of the multidimensional keys might not be defined (set to NULL),
 * meaning that they have to be treated as wildcards (in such cases ordering is required).
 *
 * Records with the same key may be returned in any order, but in a manner that n records
 * with the same key are retrieved by n successive GetNext() calls.
 *
 * When called without passing a valid transaction handle the iterator will be wrapped in its
 * own transaction which will be closed when CloseIterator() is called.
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the iterator was successfully created
 * - \ref kErrorUnknownIndex
 *     -->   if the given index has been closed or never existed
 * - \ref kErrorIncompatibleKey
 *     -->   if one or both given keys is not compatible with the given index
 * - \ref kErrorDeadlock
 *     -->   if the call could not be completed because of a deadlock
 * - \ref kErrorGenericFailure
 *     -->   if the records could not be retrieved for some other reason
 */
ErrorCode GetRecords(Transaction *tx, Index *idx, Key min_keys, Key max_keys, Iterator **it) {
    // Check that all input values are valid
    if ((idx == NULL) || idx->isClosed())
        return kErrorUnknownIndex;

    if (it == NULL)
        return kErrorGenericFailure;

    if (!idx->getSchema()->Compatible(min_keys) || !idx->getSchema()->Compatible(max_keys))
        return kErrorIncompatibleKey;

    try {

        Key copyMinKey, copyMaxKey; //Why Copy Keys without using it??
        CopyKey(copyMinKey, min_keys);
        CopyKey(copyMaxKey, max_keys);
        *it = new Iterator(idx, min_keys, max_keys);


        // Creating implicit short term transaction 
        ErrorCode ret;
        if (tx == NULL) //Modified by Basmah @ 11 March,2012 12:12 am ,   //Reason: We first check if this is autocomit case then create 
        { // new tx, else no need ,so i added if condition                                   
            Transaction *tx_a;
            ret = BeginTransaction(&tx_a);
            if (ret != kOk)
                return kErrorGenericFailure;
            tx_a->Tx_set_autoCommit();
            if (tx_a->isReject())
                return kErrorGenericFailure;
            if (tx_a->Tx_index() == NULL)
                tx_a->Tx_set_index(idx);
            // Locking the index to do get record at line ^@) create a deadlock with myself :)
            if (!(tx_a->Tx_isHandled()))
                idx->HandleTransaction(&tx_a);

            // moving LOCK @ March 10; 9:53 PM 

            lock(idx->getSchema()->mutex_) {
                idx->Search(tx_a, copyMinKey, copyMaxKey, it);
            }

            // Committing implicit short term transaction @ March 10; 9:53 PM
            if (ret == kOk)
                ret = AutoCommitEndTransaction(&tx_a);
            if (ret != kOk)
                return kErrorGenericFailure;
        } else {
            if (tx->Tx_index() == NULL)
                tx->Tx_set_index(idx);

            // Locking the index to do get record at line ^@) create a deadlock with myself :)
            if (!(tx->Tx_isHandled()))
                idx->HandleTransaction(&tx);

            lock(idx->getSchema()->mutex_) {
                idx->Search(tx, min_keys, max_keys, it);
            }
            //Retrieve all the valid records that's present in Transaction's Shadow Space
            // TODO
        }

    } catch (std::bad_alloc &e) {
        if (*it)
            (*it)->Close();
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        if (*it)
            (*it)->Close();
        return e;
    } catch (...) {
        if (*it)
            (*it)->Close();
        return kErrorGenericFailure;
    }

    return kOk;
}

/**
 * Moves the \ref Iterator to the next record or reports an end of range (by returning
 * an \ref kErrorNotFound status), if the maximum multidimensional key is exceeded.
 *
 * Records are returned in ascending order by their key. Records with the same key
 * may be returned in any order (see GetRecords() for details).
 *
 * The iterator needs to guarantee that the read data will not be changed by another transaction
 * until the iterator is either moved to the next record or is closed.
 *
 * @return ErrorCode
 * - \ref kOk
 *     -->   if the next record was successfully retrieved
 * - \ref kErrorIteratorClosed
 *     -->   if the given iterator has been closed already or never existed
 * - \ref kErrorNotFound
 *     -->   if a follow-up record could not be found
 * - \ref kErrorDeadlock
 *     -->   if the call could not be completed because of a deadlock
 * - \ref kErrorGenericFailure
 *     -->   if the operation did not complete for some other reason
 */
ErrorCode GetNext(Iterator *it, Record** record) {
    //TODO unlock previous record and lock next.
    if (it == NULL)
        return kErrorGenericFailure;
    if (it->closed()) {
        return kErrorIteratorClosed;
    }
    try {
        if (it->next()) {
            *record = it->value();
            return kOk;
        } else {
            return kErrorNotFound;
        }
    } catch (std::bad_alloc &e) {
        return kErrorOutOfMemory;
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

ErrorCode CloseIterator(Iterator **it) {
    try {
        (*it)->Close();
    } catch (ErrorCode &e) {
        return e;
    } catch (...) {
        return kErrorGenericFailure;
    }
    return kOk;
}

